﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Net.Sockets;
using System.Net;
using System.Threading;

namespace CoreLib.Communication.Sockets
{
    /// <summary>
    /// Layer over the Socket class using InterNetwork addressfamily and Tcp protocol
    /// Requires a header on the packets with structure:
    /// [0] = Packet length as ushort
    /// </summary>
    public class SocketServer
    {
        //the socket that will receive all the incoming connections
        private Socket socketServer { get; set; }

        /// <summary>
        /// Delegates invoked when a client connects
        /// </summary>
        public ConnectionEvent Connected;

        /// <summary>
        /// Delegates invoked when a client received a packet
        /// </summary>
        public TransferEvent Received;

        /// <summary>
        /// Delegates invoked when a client sent a packet
        /// </summary>
        public TransferEvent Sent;

        /// <summary>
        /// Delegates invoked when a client is disconnected
        /// </summary>
        public ConnectionEvent Disconnected;

        /// <summary>
        /// Gets the port that the SocketServer is listening
        /// </summary>
        public int Port
        {
            get
            {
                return int.Parse(socketServer.LocalEndPoint.ToString().Split(':')[1]);
            }
        }

        /// <summary>
        /// Sets or gets the additional length that will be added to the received packets
        /// </summary>
        public int PaddingLength { get; set; }

        /// <summary>
        /// Creates a new SocketServer instance
        /// </summary>
        /// <param name="port">Port that will be listened by the SocketServer</param>
        public SocketServer(int port)
        {
            this.socketServer = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
            this.socketServer.Bind(new IPEndPoint(IPAddress.Any, port));
        }

        /// <summary>
        /// Start accepting the incoming connections requests
        /// </summary>
        /// <param name="backlog">the max length of the pending connections queue</param>
        public void BeginListen(int backlog)
        {
            socketServer.Listen(backlog);
            socketServer.BeginAccept(Accept_Callback, null);
        }

        //callback for the Socket.BeginAccept method
        private void Accept_Callback(IAsyncResult result)
        {
            try
            {
                if (result.IsCompleted)
                {
                    SocketClient cSocket = new SocketClient
                        (socketServer.EndAccept(result), this);
                }
            }
            finally
            {
                socketServer.BeginAccept(Accept_Callback, null);
            }
        }
    }
}
